{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Pandas nlargest() 和 nsmallest() - 快速获取最大最小值\n\n本教程详细介绍如何使用 `nlargest()` 和 `nsmallest()` 方法快速获取最大值和最小值。\n\n## 目录\n1. nlargest() 和 nsmallest() 基础用法\n2. DataFrame 中的应用\n3. Series 中的应用\n4. 多列排序\n5. 处理重复值\n6. 性能对比\n7. 实际应用场景"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 导入库"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import pandas as pd\nimport numpy as np\nimport time\nimport warnings\nwarnings.filterwarnings('ignore')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 1. nlargest() 和 nsmallest() 基础用法\n\n### 方法说明\n\n`nlargest()` 和 `nsmallest()` 是快速获取最大值和最小值的高效方法。\n\n**语法:**\n```python\n# DataFrame\ndf.nlargest(n, columns, keep='first')\ndf.nsmallest(n, columns, keep='first')\n\n# Series\ns.nlargest(n, keep='first')\ns.nsmallest(n, keep='first')\n```\n\n**主要参数:**\n- `n`: 返回的行数\n- `columns`: 排序的列名（DataFrame 中）\n- `keep`: 处理重复值的方式，'first', 'last', 'all'\n\n**特点:**\n- ✅ 比 sort_values() + head() 更高效\n- ✅ 专门用于获取极值\n- ✅ 内部使用堆排序算法\n- ✅ 支持处理重复值\n\n**适用场景:** 需要快速获取前 N 个最大/最小值时"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 示例1: 创建示例数据"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 创建学生成绩数据\ndf = pd.DataFrame({\n    '姓名': ['张三', '李四', '王五', '赵六', '孙七', '周八', '吴九', '郑十'],\n    '数学': [85, 92, 78, 96, 88, 90, 82, 94],\n    '英语': [90, 85, 95, 82, 91, 87, 89, 93],\n    '语文': [88, 90, 85, 94, 87, 92, 86, 91],\n    '总分': [263, 267, 258, 272, 266, 269, 257, 278]\n})\n\nprint(\"学生成绩数据:\")\nprint(df)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 示例2: 获取最高分的前3名\n\n使用 nlargest() 获取总分最高的前3名学生。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "top3 = df.nlargest(3, '总分')\nprint(\"总分前3名:\")\nprint(top3)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 示例3: 获取最低分的前3名\n\n使用 nsmallest() 获取总分最低的前3名学生。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "bottom3 = df.nsmallest(3, '总分')\nprint(\"总分后3名:\")\nprint(bottom3)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 2. DataFrame 中的应用\n\n在 DataFrame 中使用 nlargest() 和 nsmallest() 的各种场景。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 示例1: 按不同科目排序"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 数学成绩前3名\nmath_top3 = df.nlargest(3, '数学')\nprint(\"数学成绩前3名:\")\nprint(math_top3[['姓名', '数学']])\n\n# 英语成绩后2名\nenglish_bottom2 = df.nsmallest(2, '英语')\nprint(\"\\n英语成绩后2名:\")\nprint(english_bottom2[['姓名', '英语']])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 示例2: 获取单个最值\n\n获取单个最大值或最小值。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 总分最高的学生\ntop1 = df.nlargest(1, '总分')\nprint(\"总分最高的学生:\")\nprint(top1[['姓名', '总分']])\n\n# 数学成绩最低的学生\nmath_lowest = df.nsmallest(1, '数学')\nprint(\"\\n数学成绩最低的学生:\")\nprint(math_lowest[['姓名', '数学']])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 示例3: 返回所有列信息\n\n获取完整的学生信息。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 总分前5名的完整信息\ntop5_full = df.nlargest(5, '总分')\nprint(\"总分前5名完整信息:\")\nprint(top5_full)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 3. Series 中的应用\n\n在 Series 中使用 nlargest() 和 nsmallest()。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 方法说明\n\nSeries 的 nlargest() 和 nsmallest() 更简单，不需要指定列名。\n\n**语法:**\n```python\ns.nlargest(n, keep='first')\ns.nsmallest(n, keep='first')\n```\n\n**特点:**\n- 直接对 Series 的值进行排序\n- 保留原始索引\n- 返回 Series 对象\n\n**适用场景:** 对单列数据进行排序"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 示例1: Series 基础用法"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 提取数学成绩 Series\nmath_scores = df.set_index('姓名')['数学']\nprint(\"数学成绩 Series:\")\nprint(math_scores)\n\n# 最高的3个成绩\ntop3_math = math_scores.nlargest(3)\nprint(\"\\n数学成绩前3名:\")\nprint(top3_math)\n\n# 最低的2个成绩\nbottom2_math = math_scores.nsmallest(2)\nprint(\"\\n数学成绩后2名:\")\nprint(bottom2_math)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 示例2: 与其他操作结合"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 计算平均分\ndf['平均分'] = df[['数学', '英语', '语文']].mean(axis=1)\n\n# 平均分最高的3名\navg_series = df.set_index('姓名')['平均分']\ntop3_avg = avg_series.nlargest(3)\nprint(\"平均分前3名:\")\nprint(top3_avg)\n\n# 转换为百分制\nprint(\"\\n转换为百分制:\")\nprint(top3_avg.round(1))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 4. 多列排序\n\n使用多个列进行排序的高级用法。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 方法说明\n\n虽然 nlargest() 和 nsmallest() 主要用于单列排序，但可以通过组合使用实现多列排序效果。\n\n**策略:**\n1. 先按主要列排序\n2. 对结果再按次要列排序\n3. 或者创建组合列\n\n**适用场景:** 需要多级排序时"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 示例1: 先按总分再按数学排序"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 方法1: 分步排序\n# 先获取总分前5名\ntop5_total = df.nlargest(5, '总分')\nprint(\"总分前5名:\")\nprint(top5_total[['姓名', '总分', '数学']])\n\n# 在前5名中按数学成绩排序\ntop5_math_sorted = top5_total.nlargest(5, '数学')\nprint(\"\\n前5名中按数学成绩排序:\")\nprint(top5_math_sorted[['姓名', '总分', '数学']])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 示例2: 创建组合排序列"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 创建组合排序列（总分*1000 + 数学分）\ndf['组合分'] = df['总分'] * 1000 + df['数学']\nprint(\"添加组合分列:\")\nprint(df[['姓名', '总分', '数学', '组合分']])\n\n# 按组合分排序\ntop3_combined = df.nlargest(3, '组合分')\nprint(\"\\n按组合分前3名:\")\nprint(top3_combined[['姓名', '总分', '数学', '组合分']])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 5. 处理重复值\n\n当数据中有重复值时的处理方法。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 方法说明\n\n`keep` 参数控制如何处理重复值。\n\n**选项:**\n- `'first'`: 保留第一个出现的（默认）\n- `'last'`: 保留最后一个出现的\n- `'all'`: 保留所有重复值\n\n**特点:**\n- 影响返回结果的行数\n- 'all' 可能返回超过 n 行\n- 对排名分析很重要\n\n**适用场景:** 数据中存在相同值时"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 示例1: 创建包含重复值的数据"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 创建包含重复分数的数据\ndf_dup = pd.DataFrame({\n    '姓名': ['A', 'B', 'C', 'D', 'E', 'F'],\n    '分数': [95, 90, 95, 88, 90, 92]\n})\n\nprint(\"包含重复分数的数据:\")\nprint(df_dup)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 示例2: 不同 keep 参数的效果"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# keep='first' (默认)\ntop3_first = df_dup.nlargest(3, '分数', keep='first')\nprint(\"keep='first' - 前3名:\")\nprint(top3_first)\n\n# keep='last'\ntop3_last = df_dup.nlargest(3, '分数', keep='last')\nprint(\"\\nkeep='last' - 前3名:\")\nprint(top3_last)\n\n# keep='all'\ntop3_all = df_dup.nlargest(3, '分数', keep='all')\nprint(\"\\nkeep='all' - 前3名:\")\nprint(top3_all)\nprint(f\"返回了 {len(top3_all)} 行数据\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 示例3: 实际排名应用"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 获取所有95分的学生\nscore_95 = df_dup[df_dup['分数'] == 95]\nprint(\"所有95分的学生:\")\nprint(score_95)\n\n# 使用 nlargest 获取前2名（包含并列）\ntop2_all = df_dup.nlargest(2, '分数', keep='all')\nprint(\"\\n前2名（包含并列）:\")\nprint(top2_all)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 6. 性能对比\n\nnlargest/nsmallest 与其他方法的性能对比。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 性能测试: 不同方法获取前10名"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 创建大型数据集\nlarge_df = pd.DataFrame({\n    '数值': np.random.randint(1, 10000, 50000),\n    '索引': range(50000)\n})\n\nn = 10  # 获取前10名\n\n# 方法1: nlargest\nstart_time = time.time()\nresult1 = large_df.nlargest(n, '数值')\ntime_nlargest = time.time() - start_time\n\n# 方法2: sort_values + head\nstart_time = time.time()\nresult2 = large_df.sort_values('数值', ascending=False).head(n)\ntime_sort_head = time.time() - start_time\n\n# 方法3: sort_values + tail (for nsmallest)\nstart_time = time.time()\nresult3 = large_df.sort_values('数值').head(n)\ntime_sort_tail = time.time() - start_time\n\nprint(f\"数据规模: {len(large_df)} 行\")\nprint(f\"获取前 {n} 名的性能对比:\")\nprint(f\"nlargest():           {time_nlargest:.6f} 秒\")\nprint(f\"sort_values()+head(): {time_sort_head:.6f} 秒\")\nprint(f\"sort_values()+head(): {time_sort_tail:.6f} 秒\")\nprint(f\"\\nnlargest() 比 sort_values() 快 {time_sort_head/time_nlargest:.1f} 倍\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 内存使用对比"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 内存使用测试\nimport sys\n\n# nlargest 只返回需要的行\nresult_nlargest = large_df.nlargest(10, '数值')\nmemory_nlargest = sys.getsizeof(result_nlargest)\n\n# sort_values 需要排序整个 DataFrame\nresult_sorted = large_df.sort_values('数值', ascending=False)\nmemory_sorted = sys.getsizeof(result_sorted)\n\nprint(f\"内存使用对比:\")\nprint(f\"nlargest(10) 结果: {memory_nlargest} 字节\")\nprint(f\"sort_values() 结果: {memory_sorted} 字节\")\nprint(f\"nlargest() 节省内存: {(memory_sorted - memory_nlargest) / memory_sorted * 100:.1f}%\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 7. 实际应用场景\n\nnlargest() 和 nsmallest() 在实际数据分析中的应用。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 场景1: 销售数据分析"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 创建销售数据\nsales_data = pd.DataFrame({\n    '销售员': ['张三', '李四', '王五', '赵六', '孙七', '周八', '吴九', '郑十'],\n    '销售额': [150000, 180000, 120000, 200000, 160000, 140000, 190000, 170000],\n    '客户数': [50, 60, 40, 70, 55, 45, 65, 58],\n    '地区': ['北京', '上海', '广州', '深圳', '杭州', '成都', '武汉', '西安']\n})\n\nprint(\"销售数据:\")\nprint(sales_data)\n\n# 销售额前3名\ntop_sales = sales_data.nlargest(3, '销售额')\nprint(\"\\n销售额前3名:\")\nprint(top_sales[['销售员', '销售额', '地区']])\n\n# 客户数最少的2名\nlow_customers = sales_data.nsmallest(2, '客户数')\nprint(\"\\n客户数最少的2名:\")\nprint(low_customers[['销售员', '客户数', '地区']])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 场景2: 股票数据分析"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 创建股票数据\nstock_data = pd.DataFrame({\n    '股票代码': ['000001', '000002', '600000', '600036', '000858', '002415'],\n    '股票名称': ['平安银行', '万科A', '浦发银行', '招商银行', '五粮液', '海康威视'],\n    '涨跌幅': [2.5, -1.8, 3.2, 1.9, -2.1, 4.1],\n    '成交量': [1200000, 800000, 1500000, 900000, 600000, 1100000],\n    '市值': [3200, 2800, 2100, 4500, 3800, 2900]\n})\n\nprint(\"股票数据:\")\nprint(stock_data)\n\n# 涨幅最大的3只股票\ntop_gainers = stock_data.nlargest(3, '涨跌幅')\nprint(\"\\n涨幅最大的3只股票:\")\nprint(top_gainers[['股票名称', '涨跌幅']])\n\n# 成交量最大的2只股票\nhigh_volume = stock_data.nlargest(2, '成交量')\nprint(\"\\n成交量最大的2只股票:\")\nprint(high_volume[['股票名称', '成交量']])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 场景3: 网站流量分析"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 创建网站流量数据\ntraffic_data = pd.DataFrame({\n    '页面': ['首页', '产品页', '关于我们', '联系我们', '新闻页', '帮助页'],\n    '访问量': [15000, 8000, 2000, 1500, 5000, 3000],\n    '停留时间': [120, 180, 90, 60, 150, 200],\n    '跳出率': [0.3, 0.2, 0.6, 0.8, 0.4, 0.5]\n})\n\nprint(\"网站流量数据:\")\nprint(traffic_data)\n\n# 访问量最高的3个页面\ntop_pages = traffic_data.nlargest(3, '访问量')\nprint(\"\\n访问量最高的3个页面:\")\nprint(top_pages[['页面', '访问量']])\n\n# 跳出率最低的2个页面（用户体验最好）\nlow_bounce = traffic_data.nsmallest(2, '跳出率')\nprint(\"\\n跳出率最低的2个页面:\")\nprint(low_bounce[['页面', '跳出率']])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 场景4: 考试成绩分析"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 创建考试成绩数据\nexam_data = pd.DataFrame({\n    '学号': ['2023001', '2023002', '2023003', '2023004', '2023005', '2023006'],\n    '姓名': ['张三', '李四', '王五', '赵六', '孙七', '周八'],\n    '数学': [95, 87, 92, 78, 89, 94],\n    '物理': [88, 91, 85, 82, 90, 87],\n    '化学': [92, 89, 88, 85, 93, 91]\n})\n\n# 计算总分\nexam_data['总分'] = exam_data[['数学', '物理', '化学']].sum(axis=1)\n\nprint(\"考试成绩数据:\")\nprint(exam_data)\n\n# 各科前2名\nsubjects = ['数学', '物理', '化学', '总分']\nfor subject in subjects:\n    top2 = exam_data.nlargest(2, subject)\n    print(f\"\\n{subject}前2名:\")\n    print(top2[['姓名', subject]])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 总结\n\n### nlargest() 和 nsmallest() 优势\n\n| 优势 | 说明 |\n|------|------|\n| **性能高** | 使用堆排序，比完整排序快 |\n| **内存省** | 只返回需要的行数 |\n| **使用简单** | 一行代码获取结果 |\n| **处理重复** | 灵活的重复值处理选项 |\n\n### 方法对比\n\n| 需求 | 推荐方法 | 原因 |\n|------|---------|------|\n| 前/后 N 名 | nlargest/nsmallest | 性能最优 |\n| 完整排序 | sort_values | 需要所有排序结果 |\n| 复杂排序 | sort_values | 支持多列、自定义 |\n| 单列极值 | Series.nlargest/nsmallest | 简单直接 |\n\n### 参数总结\n\n| 参数 | 类型 | 说明 | 默认值 |\n|------|------|------|--------|\n| `n` | int | 返回行数 | 必需 |\n| `columns` | str/list | 排序列名 | 必需(DataFrame) |\n| `keep` | str | 重复值处理 | 'first' |\n\n### 关键要点\n\n1. **性能优势**: 获取少量极值时比 sort_values() 快\n2. **内存效率**: 只返回需要的行，节省内存\n3. **重复值处理**: keep='all' 可能返回超过 n 行\n4. **适用场景**: Top-N 分析、排行榜、异常值检测\n5. **Series 支持**: 对单列数据更简单\n6. **组合使用**: 可以与其他 Pandas 操作链式调用\n\n### 最佳实践\n\n```python\n# ✅ 推荐\ndf.nlargest(10, '销售额')  # 获取前10名\ns.nsmallest(5)  # Series 前5小值\ndf.nlargest(3, '分数', keep='all')  # 处理并列情况\n\n# ❌ 避免\ndf.sort_values('销售额').tail(10)  # 获取前10名时效率低\n```"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "name": "python",
   "version": "3.8.0"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}